#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Proyecto 2 Entrega 2
# lenguaje para graficar funciones y datos
# Traductores e Interpretadores (CI-3725)
# Autores: Carlos Jose Chitty   07-41896
#          Alejandro Rodriguez  04-37507

import sys
import lex
import yacc
import string

__author__ = "Alejandro Rodriguez & Carlos Chitty"
__date__ = "$Jun 28, 2010 9:05:37 PM$"

# Definiendo los tokens
tokens = (
          'NUMEROS','VARIABLES','PARENTESISDER', 'PARENTESISIZQ', 'RANGE',
          'MAS', 'MENOS', 'POR', 'ENTRE', 'POTENCIA', 'CORCHETEIZQ', 'CORCHETEDER', 'COMA',
          'SIN', 'COS', 'TAN', 'EXP', 'LOG', 'CEIL', 'FLOOR',
          'IGUAL',
          'LLAVEIZQ', 'LLAVEDER', 'LINES', 'POINTS', 'LINESPOINTS', 'PLOT', 'PUNTOYCOMA',
          )

# Palabras reservadas
reserved = {
                'lines':'LINES',
                'points':'POINTS',
                'linespoints':'LINESPOINTS',
                'with':'WITH',
                'range':'RANGE',
                'plot':'PLOT',
                'sin':'SIN',
                'cos':'COS',
                'tan':'TAN',
                'exp':'EXP',
                'log':'LOG',
                'ceil':'CEIL',
                'floor':'FLOOR',
}

t_ignore = ' \t'

# Definiendo las gramaticas
# Gramatica para numeros
def t_NUMEROS(t):
    r'[0-9]+(\.[0-9]+(e[+-][0-9]+)?)?'
    return t

# Gramatica para variables
def t_VARIABLES(t):
    r'[A-Za-z]+'
    t.type = reserved.get(t.value,'VARIABLES')
    return t

#def t_PUSHBACK(t):
#    r'push_back'
#    t.type = reserved.get(t.value,'PUSHBACK')
#    return t

def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)

# Gramatica para parentesis
t_PARENTESISIZQ = r'\('
t_PARENTESISDER = r'\)'
t_CORCHETEIZQ = r'\['
t_CORCHETEDER = r'\]'
t_LLAVEIZQ = r'{'
t_LLAVEDER = r'{'
t_PUNTOYCOMA = r';'

#t_FILE = r'\'.*\''
# Gramatica para signos de expresion matematica
t_MAS = r'\+'
t_MENOS = r'\-'
t_POR = r'\*'
t_ENTRE = r'\/'
t_POTENCIA = r'\^'
t_COMA = r','
t_IGUAL = r'='

# Definicion para casos de error
def t_error(t):
    print "Caracter inválido '%s'" % t.value[0] + '. Linea: ' + str(t.lineno) + '. Columna ' + str(t.lexpos) + '.'
    sys.exit()


lexar = lex.lex()

# Precedencia de operadores
precedence = (
    ('left','MAS','MENOS'),
    ('left','POR','ENTRE'),
    ('left','POTENCIA'),
    ('right','COMA'),
    )

# Aqui definimos las clases para el arbol sintactico
class Arbol:
    def __init__(self,hijo=[]):
        self.hijo = hijo

    def __repr__(self):
        return "%s" %self.hijo

class Entero:
    def __init__(self,hijo=[]):
        self.hijo = hijo

    def __repr__(self):
        return "%s" %self.hijo

class Variable:
    def __init__(self,hijo=[]):
        self.hijo = hijo

    def __repr__(self):
        return "%s" %self.hijo

class Arreglo:
    def __init__(self,hijo=[]):
        self.hijo = hijo

    def __repr__(self):
            return "[%s]" %self.hijo

class Parentesis:
    def __init__(self,hijo=[]):
        self.hijo = hijo

    def __repr__(self):
        return "(%s)" % self.hijo

class OperadorAritmetico:
    def __init__(self,hijo=[],op=''):
        self.hijo = hijo
        self.op = op

    def __repr__(self):
        
        if self.hijo[1] == '+':
            return "(%s+%s)" % (self.hijo[0],self.hijo[2])
        elif self.hijo[1]=='-':
            
            if len(self.hijo) == 3:
                return "(%s-%s)" %(self.hijo[0],self.hijo[2])
            else:
                return "-(%s)" % self.hijo[0]
        elif self.hijo[1]=='*':
            return "(%s*%s)" %(self.hijo[0],self.hijo[2])
        elif self.hijo[1]=='/':
            return "(%s/%s)" %(self.hijo[0],self.hijo[2])
        elif self.hijo[1]=='^':
            return "(%s**%s)" %(self.hijo[0],self.hijo[2])
        
class Plot:
    def __init__(self,hijo=[]):
        self.hijo = hijo
        self.variable = ""
        if not self.verExpGraficable():
            print "Expresion invalida!"
            sys.exit(-1)
            
    def __repr__(self):
        if len(self.hijo) ==3:
            return "plot %s %s %s" % (self.hijo[0],self.hijo[1],self.hijo[2])
        elif len(self.hijo) == 2:
            return "plot %s %s" % (self.hijo[0],self.hijo[1])
        return ""

    def imprimible(self):
        if len(self.hijo) == 2:
            temp = ""
            if esTipo(self.hijo[1], Arreglo):
                if esTipo(self.hijo[1].hijo, ElementoArreglo):
                    for x in self.hijo[1].hijo:
                        temp1 = "%s" % (x)
                        x = string.replace(temp1, self.variable, "x")
                        temp = "%s, %s" % (x, temp)
                temp = temp[0:len(temp)-2]
            else:
                temp1 = "%s" % (self.hijo[1])
                x = string.replace(temp1, self.variable, "x")
                temp = "%s" % (x)

        elif len(self.hijo) == 3:
            temp = ""
            if esTipo(self.hijo[1], Arreglo):
                if esTipo(self.hijo[1].hijo, ElementoArreglo):
                    if esTipo(self.hijo[2], ArregloEstilo):
                        if len(self.hijo[2].hijo) == len(self.hijo[1].hijo):
                            estilo = iter(self.hijo[2].hijo)
                            for x in self.hijo[1].hijo:
                                temp1 = "%s" % (x)
                                x = string.replace(temp1, self.variable, "x")
                                temp = "%s with %s, %s" % (x, estilo.next(), temp)
                            temp = temp[0:len(temp)-2]
                        else:
                            print "El lenano de los arreglos de estilo y de expresiones no es el mismo"
                            sys.exit(1)
                    else:
                        print "Los estilos no son una expresion graficable"
                        sys.exit(-1)
            else:
                if len(self.hijo) == 2:
                    temp1 = "%s" % (self.hijo[1])
                    x = string.replace(temp1, self.variable, "x")
                    temp = "%s" % (x)
                elif len(self.hijo) == 3:
                    if esTipo(self.hijo[2], ArregloEstilo):
                        print "Solo hay un expresion graficable"
                        sys.exit(-1)
                    temp1 = "%s" % (self.hijo[1])
                    x = string.replace(temp1, self.variable, "x")
                    temp = "%s with %s" % (x, self.hijo[2])
        return temp

    def verExpGraficable(self):

        if esTipo(self.hijo[1], Arreglo):

            for x in self.hijo[1].hijo:
                temp = "%s" % (x)
                variables = re.findall(r'[a-zA-Z]+', temp)

                # Recorre la lista de variables.
                for x in variables:

                    if self.variable == "":
                        self.variable = x
                        continue

                    regex = r"^%s$" % (self.variable)

                    # Aqui verificamos si las variables son iguales a las de la declaracion.
                    if re.search(regex, x) == None:
                        print "Hay una variable que no esta declarada"
                        sys.exit(-1)
        else:

            temp = "%s" % (self.hijo[1])
            variables = re.findall(r'[a-zA-Z]+', temp)

            # Aqui recorremos la lista.
            for x in variables:
                if self.variable == "":
                    self.variable = x
                    continue
                regex = r"^%s$" % (self.variable)

                # Aqui verificamos que las variables de la expresion sean iguales a las de la declaracion
                if re.search(regex, x) == None:
                    print "Hay una variable que no esta declarada"
                    sys.exit(-1)
        return True

class NodoArregloEstilo:
    def __init__(self, hijo=[]):
        self.hijo = hijo

    def __repr__(self):
        temp = ""
        for x in self.hijo:
            numero = "%s," % (x)
            temp = numero + temp
        temp = temp[0:len(temp)-1]
        return "[%s]" % temp

class ElementoEstilo:
    class ElementoEstiloIterator:
        def __init__(self, node):
            self.stack = [node]

        def __iter__(self):
            return self

        def next(self):
            if not self.stack:
                raise StopIteration

            node = self.stack.pop(-1)
            if esTipo(node.hijo, ElementoEstilo):
                self.stack.append(node.hijo)
            return node

    def __init__(self, elemento, hijo=None):
        self.hijo = hijo
        self.elemento = elemento

    def __repr__(self):
        return "%s" % (self.elemento)

    def __len__(self):
        contador = 0
        for x in self:
            contador += 1
        return contador

    def __iter__(self):
        return ElementoEstilo.ElementoEstiloIterator(self)

class NodoEstiloVacio:
    def __init__(self, hijo=[]):
        self.hijo = hijo

    def __repr__(self):
        return "(ESTILO VACIO)"

class NodoConstanteEstilo:
    def __init__(self, constante):
        self.constante = constante

    def __repr__(self):
        return "%s" % (self.constante)
    
class Funcion:
    def __init__(self, nombre=None, variable=None, expresion=None,actual=None):
        self.nombre = nombre
        self.expression = expresion
        self.variable = variable
        self.actual = "%s" % (actual)        

        if actual == None:
            self.actual = variable

    def __repr__(self):
        temp = "%s" % (self.expression)
        
        x = string.replace(temp, self.variable, self.actual)
        
        return "%s" % (x)

class Estilo:
    def __init__(self,hijo=[]):
        self.hijo = hijo

    def __repr__(self):
        return "(with %s)" %self.hijo

class Range:
    def __init__(self,hijo=[]):
        self.hijo = hijo

    def __repr__(self):
        return "([%s:%s])" %(self.hijo[0],self.hijo[1])



# Aqui inicializazamos la tabla de simbolos
nombres = {'pi':'3.141516', 'e':'2.718281'}

#Aqui trabajamos las instrucciones
def p_instruccionesSin(p):
    '''start : sininst
              | listaInstruccion sininst'''
    if len(p)==2:
        p[0]=([p[1]])
    if len(p)==3:
        p[0]=([p[1],p[2]])

#Aqui trabajamos las instrucciones
def p_instrucciones(p):
    '''listaInstruccion : inst
                       | listaInstruccion inst'''
    if len(p)==2:
        p[0]=([p[1]])
    if len(p)==3:
        p[0]=([p[1],p[2]])

#Aqui mostramos una forma de iniciar una funcion
def p_inst(p):
    '''inst : VARIABLES PARENTESISIZQ VARIABLES PARENTESISDER IGUAL expression PUNTOYCOMA
            | plot PUNTOYCOMA'''
    
    if len(p) == 7:
        p[0] = Funcion(p[1],p[3],p[6])
        nombres[p[1]] = p[0]

#Aqui mostramos otra forma de iniciar una funcion
def p_sininst(p):
    '''sininst : VARIABLES PARENTESISIZQ VARIABLES PARENTESISDER IGUAL expression

            | plot'''
#            | p
    if len(p) == 7:
        p[0] = Funcion(p[1],p[3],p[6])
        nombres[p[1]]=p[0]
    elif len(p) == 2:
        p[0] = p[1]

#Aqui iniciamos la funcion del plot
def p_plot(p):
    '''plot : PLOT expression COMA expressionGraficable
            | PLOT expression COMA expressionGraficable LLAVEIZQ estilo LLAVEDER'''
    if len(p) == 5:
        p[0] = Plot([p[2],p[4]])
    elif len(p) == 8:
        p[0] = Plot([p[2],p[4],p[6]])

def p_estilo(p):
    '''estilo : LINES
              | POINTS
              | LINESPOINTS'''
    p[0]=Estilo(p[1])

def p_em_variables(p):
    'expression : variables'
    p[0]=Variable([p[1]])

def p_em_mas(p):
    'expression : expression MAS expression'
    p[0]= OperadorAritmetico([p[1],p[2],p[3]])

def p_em_menos(p):
    '''expression : expression MENOS expression
                  | MENOS expression %prec COMA'''
    if len(p) == 4 :
        p[0]=OperadorAritmetico([p[1], p[2], p[3]])
    
    if len(p) == 3 :

        p[0]=OperadorAritmetico([p[2], p[1]])

def p_em_por(p):
    'expression : expression POR expression'
    p[0]=OperadorAritmetico([p[1],p[2],p[3]])

def p_em_entre(p):
    'expression : expression ENTRE expression'
    p[0]=OperadorAritmetico([p[1],p[2],p[3]])

def p_em_potencia(p):
    'expression : expression POTENCIA expression'
    p[0]=OperadorAritmetico([p[1],p[2],p[3]])

def p_em_numeros(p):
    'expression : NUMEROS'
    p[0]=Entero(p[1])

def p_em_parentesis(p):
    'expression : PARENTESISIZQ expression PARENTESISDER'
    p[0]=Parentesis(p[2])

def p_var(p):
    'variables : VARIABLES'
    p[0] = Variable(p[1])

def p_em_funciones(p):
    '''variables : fReservadas PARENTESISIZQ expression PARENTESISDER'''
    try:
        p[0] = Funcion(nombres[p[1]].nombre,nombres[p[1]].variable,nombres[p[1]].expression,p[3])
    
    except LookupError:
        print ("Expresion indefinida '%s'" % p[1])
        sys.exit(-1)

def p_em_fReservadas(p):
    '''fReservadas : SIN
                   | COS
                   | TAN
                   | EXP
                   | LOG
                   | CEIL
                   | FLOOR
                   | VARIABLES'''
    p[0]=p[1]

def p_em_arreglos(p):
    '''arreglo : CORCHETEIZQ CORCHETEDER
               | CORCHETEIZQ elementoArreglo CORCHETEDER'''
    if len (p) == 3 :
        p[0] = Arreglo()
    elif len(p) == 4 :
        p[0] = Arreglo(p[2])

def p_em_elementoArreglo(p):
    '''elementoArreglo : expression
                       | elementoArreglo COMA expression'''
    if len (p) == 4 :
        p[0] = (p[1], p[3])
    elif len(p) == 2 :
        p[0] = ('Expresion', p[1])

#def p_em_arreglosComprension(p):
#    'expression : CORCHETEIZQ expression FOR VARIABLES IN expression CORCHETEDER'
#    p[0] = ('Arreglo por comprension',p[2],('Variable',p[4]),p[6])

def p_em_range(p):
    'expression : RANGE PARENTESISIZQ expression COMA expression PARENTESISDER'
    p[0] = Range([p[3], p[5]])

def p_expression_graficable(p):
    '''expressionGraficable : expression
                            | arreglo'''
    p[0] = p[1]

def p_error(p):
    print "Se ha detectado un error."
    sys.exit(1)

parser= yacc.yacc()

if __name__ == "__main__":
    if (len(sys.argv) < 2):
	print "Debe pasar 2 parametros: la operacion y el nombre del archivo"
	sys.exit(0)
    # Tralenos de abrir el archivo de resumenes indicado como cuarto argumento.
    try:
        resumen = open(sys.argv[1], "r")
    except IOError:
        print "Archivo de resumenes no valido!"

    print "Output: "
    s = resumen.read()
    resultado = parser.parse(s,lexer = lexar)#,debug = 1)
    print resultado